"""
推荐系统相关任务
"""
import pandas as pd
import os
import json
from typing import Dict, Set
from collections import defaultdict

# 兼容未安装 celery 的环境：
# - 当仅调用本模块的纯函数（如 get_recommendations）时，不要求安装 celery
# - 当需要运行定时任务时，再安装并启动 celery
try:
    from celery_app import celery_app
except Exception:
    class _DummyCelery:  # 最简装饰器，保持函数可被正常定义与导入
        def task(self, name=None, **kwargs):
            def _decorator(fn):
                return fn
            return _decorator
    celery_app = _DummyCelery()

from utils.myredis import Mredis

# CSV文件路径
CSV_FILE_PATH = "data/goods_reviews.csv"

# Redis实例
redis_client = Mredis(db=3)  # 使用Redis数据库3存储推荐索引


@celery_app.task(name='tasks.recommendation.build_recommendation_index')
def build_recommendation_index():
    """
    构建推荐索引任务
    
    1. 读取CSV文件
    2. 构造用户-物品索引存入Redis
    3. 构造用户-用户索引存入Redis
    """
    try:
        # 检查CSV文件是否存在
        if not os.path.exists(CSV_FILE_PATH):
            print(f"[推荐索引] CSV文件不存在: {CSV_FILE_PATH}")
            return
        
        # 读取CSV文件
        df = pd.read_csv(CSV_FILE_PATH, encoding="utf-8-sig")
        
        if df.empty:
            print("[推荐索引] CSV文件为空")
            return
        
        print(f"[推荐索引] 读取到 {len(df)} 条评价数据")
        
        # 1. 构造用户-物品索引
        # 格式: user_item:{user_id} -> Set[goods_id]
        user_item_index = defaultdict(set)
        for _, row in df.iterrows():
            user_id = int(row['userid'])
            goods_id = int(row['goodsid'])
            user_item_index[user_id].add(goods_id)
        
        # 存入Redis
        for user_id, goods_set in user_item_index.items():
            key = f"user_item:{user_id}"
            # 先删除旧数据
            redis_client.str_del(key)
            # 存入新的商品ID集合（使用JSON序列化）
            goods_list = list(goods_set)
            redis_client.set_str(key, json.dumps(goods_list))
        
        print(f"[推荐索引] 用户-物品索引构建完成，共 {len(user_item_index)} 个用户")
        
        # 2. 构造用户-用户索引（基于共同评价的商品）
        # 格式: user_user:{user_id} -> Dict[similar_user_id: similarity_score]
        user_user_index = defaultdict(dict)
        
        # 计算用户相似度（基于共同评价的商品数量）
        user_list = list(user_item_index.keys())
        for i, user1 in enumerate(user_list):
            for user2 in user_list[i+1:]:
                goods1 = user_item_index[user1]
                goods2 = user_item_index[user2]
                
                # 计算交集（共同评价的商品）
                common_goods = goods1.intersection(goods2)
                if common_goods:
                    # 相似度 = 共同商品数 / (用户1商品数 + 用户2商品数 - 共同商品数)
                    similarity = len(common_goods) / (len(goods1) + len(goods2) - len(common_goods))
                    
                    # 只保存相似度大于0的用户对
                    if similarity > 0:
                        user_user_index[user1][user2] = similarity
                        user_user_index[user2][user1] = similarity
        
        # 存入Redis
        for user_id, similar_users in user_user_index.items():
            key = f"user_user:{user_id}"
            # 先删除旧数据
            redis_client.str_del(key)
            # 存入相似用户字典（使用JSON序列化）
            redis_client.set_str(key, json.dumps(similar_users))
        
        print(f"[推荐索引] 用户-用户索引构建完成，共 {len(user_user_index)} 个用户")
        
        return {
            "status": "success",
            "user_item_count": len(user_item_index),
            "user_user_count": len(user_user_index),
            "total_reviews": len(df)
        }
        
    except Exception as e:
        print(f"[推荐索引] 构建失败: {e}")
        import traceback
        traceback.print_exc()
        return {
            "status": "error",
            "error": str(e)
        }


def get_user_item_index(user_id: int) -> Set[int]:
    """从Redis获取用户的商品索引"""
    try:
        key = f"user_item:{user_id}"
        data = redis_client.get_str(key)
        if data:
            # 如果data是bytes，需要解码
            if isinstance(data, bytes):
                data = data.decode('utf-8')
            goods_list = json.loads(data)
            return set(goods_list)
        return set()
    except Exception as e:
        print(f"[推荐] 获取用户-物品索引失败 (user_id={user_id}): {e}")
        return set()


def get_user_user_index(user_id: int) -> Dict[int, float]:
    """从Redis获取用户的相似用户索引"""
    try:
        key = f"user_user:{user_id}"
        data = redis_client.get_str(key)
        if data:
            # 如果data是bytes，需要解码
            if isinstance(data, bytes):
                data = data.decode('utf-8')
            similar_users = json.loads(data)
            return {int(k): float(v) for k, v in similar_users.items()}
        return {}
    except Exception as e:
        print(f"[推荐] 获取用户-用户索引失败 (user_id={user_id}): {e}")
        return {}


def get_recommendations(user_id: int, limit: int = 10) -> list:
    """
    获取推荐商品列表
    
    算法：
    1. 获取用户-物品索引（用户已评价的商品）
    2. 获取用户-用户索引（相似用户）
    3. 找到相似度最高的用户
    4. 获取该用户的商品
    5. 取差集（推荐用户没看过的）
    """
    try:
        # 1. 获取用户已评价的商品
        user_goods = get_user_item_index(user_id)
        
        # 2. 获取相似用户
        similar_users = get_user_user_index(user_id)
        
        if not similar_users:
            # 如果没有相似用户，返回空列表
            return []
        
        # 3. 找到相似度最高的用户
        most_similar_user = max(similar_users.items(), key=lambda x: x[1])[0]
        
        # 4. 获取该用户的商品
        similar_user_goods = get_user_item_index(most_similar_user)
        
        # 5. 取差集（推荐用户没看过的）
        recommended_goods = similar_user_goods - user_goods
        
        # 返回推荐商品ID列表（限制数量）
        return list(recommended_goods)[:limit]
        
    except Exception as e:
        print(f"[推荐] 获取推荐失败: {e}")
        return []

